﻿using System;
using System.Linq;
using System.Windows;
using Shoozla.ViewModel;
using Shoozla.View;
using System.Threading;
using Shoozla.DataModel;
using System.IO;
using System.Diagnostics;
using Shoozla.DataModel.Helper;
using System.Collections.Generic;
using System.Threading.Tasks;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Drawing;
using System.Net.Configuration;
using System.Reflection;


namespace Shoozla
{

    /// <summary>
    /// Interaction logic for App.xaml
    /// </summary>
    public partial class App : System.Windows.Application
    {
        #region Enums
        public enum AppStatus {
            Idle,
            ReadingFiles,
            SearchingCovers,
            ApplyingCovers
        }
        public enum StartingMode
        { 
            Visual,
            Background
        }
        #endregion

        #region  properties

        public  static  String LogFileName = "shoozla-log.txt";
        private static  String EventSourceName = "Shoozla";
        private static bool EventSourceCreated = false;

        public static ICoverStorage CoverImageCache;
        public static CoverSearcher CoverSearcher;
        public static List<CoverSearcher> AvailableCoverSearchers;
        public static IMP3Processor Mp3Processor;
        public static StatisticsLogger Logger;
        public static TxtTraceListener Tracer;       
        public static AppStatus CurrentAppStatus;
        public static ThreadingFactorEnum ThreadingFactor = ThreadingFactorEnum.Maximum; //num of threads
        public static Mp3File CurrentEditingSong;

        public static CancellationTokenSource MainCancellationTokenSource;

        //Folder cache
        public static List<string> ReadMp3Folders;

        //views
        public static FolderListView FolderView;
        public static ID3TagControl SummaryView;
        public static MainWindow MainView;
        public static SearchingNagScreenView SearchingNagScreenWindow;
        public static ProgressBarView MainProgressBarView;

        
        //Mp3 file read
        public static MultithreadingObservableCollection<Mp3File> AllItems;

        #endregion

        protected override void OnExit(ExitEventArgs e)
        {
            base.OnExit(e);           
        }
        /// <summary>
        /// Main method, initialize all the variables and event callbacks
        /// </summary>
        /// <param name="e"></param>
        protected override void OnStartup(StartupEventArgs e)
        {
            //update statistics webservice
            //it will log only the IP + Datetime, for stat usages
            try{
                Task.Factory.StartNew(() =>
                {
                    StatisticsService.UsageStatisticsClient statClient = new StatisticsService.UsageStatisticsClient();
                    statClient.SoftwareStarted();
                });
            }
            catch
            {
                //do nothing
            }

            base.OnStartup(e);
            MainCancellationTokenSource = new CancellationTokenSource();
            ReadMp3Folders = new List<string>();
            Tracer = new TxtTraceListener(App.LogFileName);
            CurrentAppStatus = AppStatus.Idle;
            Logger = new StatisticsLogger();    
            Mp3Processor = new IDSharpProcessor();
            CoverImageCache = new InternetCoverStorage(MainCancellationTokenSource.Token);

            FindCoverSearchersAddIn();
                               
            LogEntry(DateTime.Now.ToLocalTime() + " | Shoozla started");

            Thread.Sleep(500); //splash screen

            //single-instance application
            bool mutexIsNew;
            using (Mutex m = new Mutex(true, "Shoozla", out mutexIsNew))
            {
                if (mutexIsNew)
                {
                    try
                    {
                        //first instance
                        switch (CheckStartingMode())
                        {
                            case StartingMode.Visual: { StartUI(); break; }
                            case StartingMode.Background: { StartBackground(); break; }
                        }
                    }
                    catch(Exception ue)
                    {
#if DEBUG
                        MessageBox.Show("Execption: " + ue.Message);
                        MessageBox.Show(ue.StackTrace);
#endif
                        LogEntry("[EX] Unhandled Exception: " + ue.Message);
                        LogEntry(ue.StackTrace);
                        if (ue.InnerException != null)
                        {
                            LogEntry("[EX] Unhandled Inner Exception: " + ue.InnerException.Message);
                            LogEntry(ue.InnerException.StackTrace);
                        }
                    }
                }
                else
                {
                    //another instance running
                    MessageBox.Show("another Instance of Shoozla is running.", "Shoozla", MessageBoxButton.OK, MessageBoxImage.Stop);
                    LogEntry("[EX] another Instance of Shoozla is running");
                }
            }

            Tracer.Flush();
            Tracer.Close();
            this.Shutdown();

        }

        private void FindCoverSearchersAddIn()
        {

            AvailableCoverSearchers = new List<DataModel.CoverSearcher>();
            try
            {
                //search for each DLL that implement the abstract class CoverSearcher
                String currentFolder = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location);
                String[] addiInAssemblies = Directory.GetFiles(currentFolder, "*.dll");

                List<Type> AddInTypes = new List<Type>();

                foreach (String file in addiInAssemblies)
                {
                    Assembly possibleAddIn = Assembly.LoadFrom(file);
                    foreach (Type t in possibleAddIn.GetExportedTypes())
                    {
                        if (t.IsClass && typeof(CoverSearcher).IsAssignableFrom(t) && !t.IsAbstract)
                            AddInTypes.Add(t);
                    }

                }

                //now we have all the types that implements the base class

                foreach (Type t in AddInTypes)
                {
                    CoverSearcher cs = (CoverSearcher)Activator.CreateInstance(t, new object[] { CoverImageCache, App.MainCancellationTokenSource.Token });
                    AvailableCoverSearchers.Add(cs);
                    cs.SearchCompleted += new EventHandler(CoverSearcher_SearchCompleted);
                    cs.SearchStarted += new EventHandler(CoverSearcher_SearchStarted);
                    cs.CoverFound += new CoverSearcher.CoverFoundHandler(CoverSearcher_CoverFound);
                    LogEntry("Search add-in found: " + cs.AddInName);
                }

                //assign the first found as default
                if (AvailableCoverSearchers.Count > 0)
                {
                    CoverSearcher = AvailableCoverSearchers[0];
                }
                else
                {
                    throw new Exception("No search add-ins");
                }
            }
            catch (Exception e)
            {
                LogEntry("[EX] No search add-in, closing the application");
                MessageBox.Show("Shoozla cannot start because it didn't find any available add-in, please reinstall the application", "Shoozla", MessageBoxButton.OK, MessageBoxImage.Error);
                this.Shutdown();
            }
        }

        public static StartingMode CheckStartingMode()
        {
            //no parameters --> VISUAL

            String[] pars = System.Environment.GetCommandLineArgs();
            if (pars.Length <= 1)
                return StartingMode.Visual; //first par is always the file name
            else
                return StartingMode.Background;
        }

        public static void StartUI()
        {

            FolderView = new FolderListView();
            MainView = new MainWindow(); //this depends from the previous view

            App.SearchingNagScreenWindow = new SearchingNagScreenView();

            //listening on events
            ((ViewModelBase)MainView.DataContext).FileSaved += new ViewModelBase.CoverSavedHandler(App_FileSaved);
            ((ViewModelBase)MainView.DataContext).ItemOperationStarted += new ViewModelBase.ItemOperationStartedHandler(App_ItemOperationStarted);
            ((ViewModelBase)MainView.DataContext).ItemOperationFinished += new EventHandler(App_ItemOperationFinished); //fire when files are saved
            ((ViewModelBase)MainView.DataContext).ItemRead += new EventHandler(App_ItemRead);

            ((ViewModelBase)FolderView.DataContext).ItemRead += new EventHandler(App_ItemRead);
            ((ViewModelBase)FolderView.DataContext).ItemOperationFinished += new EventHandler(App_ItemOperationFinished);
            ((ViewModelBase)FolderView.DataContext).ItemOperationStarted += new ViewModelBase.ItemOperationStartedHandler(App_ItemOperationStarted);

            App.AllItems = new MultithreadingObservableCollection<Mp3File>(MainView.Dispatcher);
            App.AllItems.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler(list_CollectionChanged);

            var viewModel = MainView.DataContext as MainWindowViewModel;

            // Allow all controls in the window to 
            // bind to the ViewModel by setting the 
            // DataContext, which propagates down 
            // the element tree.
            MainView.DataContext = viewModel;

            try
            {
                LogEntry("MAIN UI thread:" + Thread.CurrentThread.ManagedThreadId);
                MainView.ShowDialog();
            }
            catch (Exception ex)
            {
                App.LogEntry("[ERROR]: App: Generic Error: " + ex.Message);
                //TaskDialogClass.TaskDialog(
                //      new System.Windows.Interop.WindowInteropHelper((Window)App.MainView).Handle,
                //      IntPtr.Zero, "Shoozla","Generic Error", "Check internet connection\n " + ex.Message, 
                //      TaskDialogButtons.OK,
                //      TaskDialogIcon.SecurityError
                //       );
                System.Windows.MessageBox.Show(App.MainView, "Check internet connection\n " + ex.Message, "Generic Error", MessageBoxButton.OK);
                throw;
            }

        }

        static void App_ItemOperationFinished(object sender, EventArgs e)
        {
            //be sure to not display the progress bar and do not block the UI
            App.MainProgressBarView.PerfomStepsToEnd(); //this end the window
        }

        public static void StartBackground()
        {
            //i.e: command line or scheduled task
            //parameters:
            // folder - path of the folder to check

            String[] pars = System.Environment.GetCommandLineArgs();

            if (pars.Length != 2)
                ShowUsage();

            DirectoryInfo folder = GetFolderName(pars);
            if (folder == null)
                return;
            try
            {
                if (!EventLog.SourceExists(EventSourceName))
                {
                    EventLog.CreateEventSource(EventSourceName, "Application");
                    EventSourceCreated = true;
                }
                LogEvent("Shoozla started on " + folder.FullName, false, 1);
            }
            catch (InvalidOperationException invalidOp)
            {
                LogEntry("[ERROR] StartBackground: Cannot create event source");
            }
            catch (Exception e)
            {
                LogEntry("[ERROR] StartBackground: " + e.Message);
            }
            finally
            {
                BatchSearch.StartBatchSearch(folder);
            }
        }

        private static DirectoryInfo GetFolderName(string[] args)
        {
            string folderName = args[1];

            if (!Directory.Exists(folderName))
            {
                LogEntry(string.Format("Folder: \"{0}\" not found.", folderName));
                return null;
            }

            return new DirectoryInfo(folderName);
        }

        private static void ShowUsage()
        {
            LogEntry("usage: shoozla FolderName");
        }

        public static void LogEntry(string message)
        {
            Trace.WriteLine(message);
        }

        public static void LogEvent(string message, bool isError, int ID)
        {
            if(EventSourceCreated)
                EventLog.WriteEntry(EventSourceName, message, isError?EventLogEntryType.Error:EventLogEntryType.Information, ID);
        }

        public static void StopWorkingThreads()
        {
            StopAllWork();
          
            switch (App.CurrentAppStatus)
            {
                case App.AppStatus.SearchingCovers:
                    {
                        App.CoverSearcher.StopSearch();
                        break;
                    }
            }

            App.CurrentAppStatus = App.AppStatus.Idle;
        }

        public static void StopAllWork() { 
            //send the stop commands and wait to reset it
            App.MainCancellationTokenSource.Cancel();
            App.MainCancellationTokenSource.Token.WaitHandle.WaitOne();
            App.MainCancellationTokenSource = new CancellationTokenSource();        
        }
           
        private void StartNagScreenWindow(object o)
        {
            object[] param = o as object[];
            App.SearchingNagScreenWindow.ShowNagScreen();
            System.Windows.Threading.Dispatcher.Run();
        }

        private static void StartProgressBarInternal(object o)
        {
            object[] param = o as object[];
            App.MainView.InitProgressBar((int)param[0], (string)param[1]); 
        }


        #region events callbacks       

        void CoverSearcher_SearchStarted(object sender, EventArgs e)
        {
            App.CurrentAppStatus = AppStatus.SearchingCovers;
            Thread newWindowThread = new Thread(new ParameterizedThreadStart(StartNagScreenWindow));
            newWindowThread.SetApartmentState(ApartmentState.STA);
            newWindowThread.IsBackground = true;
            newWindowThread.Start(new object[] { "Searching for multiple covers" });
        }

        void CoverSearcher_SearchCompleted(object sender, EventArgs e)
        {
            App.CurrentAppStatus = AppStatus.ApplyingCovers;
            ((InternetCoverStorage)CoverImageCache).CancelToken = App.MainCancellationTokenSource.Token;
          //  ((InternetCoverStorage)CoverImageCache).DownloadImages();
            App.CurrentAppStatus = AppStatus.Idle;
           // App.StopWorkingThreads();
        }

        public static void ApplyFoundCoversToSongs()
        { 
            //go for all selected item and check if we have found the cover, if yes apply the cover
            //get all selected items (binding on IsSelected property)
            var selected = (
                from m in App.AllItems
                where m.IsSelected == true 
                select m);
          
            foreach(Mp3File m in selected)
            {
                if (m.ImageUrl != null)
                {
                    if (App.CoverImageCache.ContainsCover(m.ImageUrl.AbsoluteUri))
                    {
                        Image cover = CoverImageCache[m.ImageUrl.AbsoluteUri];
                        m.Cover = cover;
                        m.IsSelected = false;
                    }
                }
            }
        }

        void CoverSearcher_CoverFound(string displayString,SearchMethod method,Uri urlFound)
        {
            App.Logger.CoversFound++;
        }

        public static void list_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
        {
            //OnPropertyChanged("App.AllItems");
            //OnPropertyChanged("CurrentItems");
            //OnPropertyChanged("Mp3WithCovers");
        }
        #endregion

        /// <summary>
        /// return true if there is at least one song dirty that need to be saved
        /// </summary>
        /// <returns></returns>
        public static bool IsCollectionDirty()
        {
            return App.AllItems.Any<Mp3File>((m) =>  m.Dirty);
        }

        private static void App_ItemOperationStarted(int num, string action) {
                Thread newWindowThread = new Thread(new ParameterizedThreadStart(StartProgressBarInternal));
                newWindowThread.SetApartmentState(ApartmentState.STA);
                newWindowThread.IsBackground = true;
                newWindowThread.Start(new object[] { num, action });
        }

        private static void App_FileSaved(string filename) {
                App.Logger.FilesSaved++;
        }

        private static void App_ItemRead(object sender, EventArgs e)
        {
                App.Logger.FilesProcessed++;
                if (App.MainProgressBarView != null)
                    App.MainProgressBarView.PerformStep();
                
                App.MainView.IncrementTaskBar();
        }
    }
}
